package com.share.tool.generator.api_url;

import com.alibaba.fastjson.JSONObject;
import com.share.common.utils.*;
import com.share.tool.generator.api.config.DirClassLoader;
import com.share.tool.generator.api.config.MyClassLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生成所有controller接口的请求url接口列表
 */
@Slf4j
public class MainApiUrl {
    public static void main(String[] args) {
        String projectDir = EnvUtil.USER_DIR;
        List<File> list = FileUtil.listFile(projectDir, "target", ".class").stream().filter(f -> {
            String path = f.getAbsolutePath();
            boolean d1 = path.endsWith("Controller.class");
            boolean d2 = path.endsWith("Web.class");
            boolean d3 = path.endsWith("Ctrl.class");
            if (d1 || d2 || d3) {
                return true;
            } else {
                return false;
            }
        }).collect(Collectors.toList());

        //特殊处理
        for (File file : list) {
            String path = file.getAbsolutePath();
            if (path.contains("share-dubbo-es-web\\target")) {
                String webhome = path.split("\\\\classes\\\\")[0] + "\\\\classes\\\\";
                String modleHomePath = webhome.replace("share-dubbo-es-web", "share-dubbo-es-service");
                List<File> modelFiles = FileUtil.listFile(modleHomePath, null, ".class");
                for (File modleFile : modelFiles) {
                    String modleFileName = modleFile.getAbsolutePath().split("\\\\classes\\\\")[1];
                    File webFilePath = new File(webhome + modleFileName);
                    FileUtil.mkdir(webFilePath);
                    FileUtil.copyFile(modleFile, webFilePath, true);
                }
            }
        }

        //加载所有依赖
        String mavenRepository = MavenUtil.getMavenRepository();
        DirClassLoader.loadJar(mavenRepository + File.separator + "de");
        DirClassLoader.loadJar(mavenRepository + File.separator + "org");
        DirClassLoader.loadJar(mavenRepository + File.separator + "com" + File.separator + "alipay");
        DirClassLoader.loadJar(mavenRepository + File.separator + "com" + File.separator + "theokanning");
        DirClassLoader.loadJar(mavenRepository + File.separator + "com" + File.separator + "share");

        Set<String> sets = new TreeSet<>();
        Map<String, String> ipPortMap = new HashMap<>();
        for (File file : list) {
            String path = file.getAbsolutePath();
            String[] split = path.split("\\\\classes\\\\");
            String classPath = split[0] + "\\\\classes\\\\";
            String className = split[1].replaceAll("\\\\", ".");
            className = className.substring(0, className.length() - ".class".length());
            ClassLoader loader = new MyClassLoader(classPath);

            Class<?> aClass = null;
            try {
                aClass = loader.loadClass(className);
            } catch (ClassNotFoundException e) {
                //model直接依赖是无法加载的
                log.error("异常path={},classPath={},className={}", path, classPath, className, e);
                continue;
            }

            String http = calcIpPort(ipPortMap, classPath);
            List<String> analysis = analysis(http, aClass);
            sets.addAll(analysis);
        }

        StringBuffer strHtml = new StringBuffer();
        strHtml.append("<!DOCTYPE html>\n" + "<html lang=\"en\">\n" + "<head>\n" + "    <meta charset=\"UTF-8\">\n" + "    <title>接口</title>\n" + "</head>\n" + "<body>");

        for (String url : sets) {
            String link = String.format("<A HREF=\"%s\">%s</A><DT>", url, url);
            strHtml.append(link);
            strHtml.append(System.lineSeparator());
            log.info(link);
        }
        strHtml.append("</body>" + "</html>");

        String idxFile = projectDir + "/index.html";
        FileUtil.write(idxFile, strHtml.toString(), true);
        log.info("接口文档生产成功idxFile={}", idxFile);
    }

    private static String calcIpPort(Map<String, String> ipPortMap, String classPath) {
        String clazzPath = ipPortMap.get(classPath);
        if (clazzPath != null) {
            return clazzPath;
        }

        String port = "";
        String context = "";
        Properties properties = FileUtil.readProperties(classPath + "/" + "application.properties");
        if (properties != null) {
            String env = properties.getProperty("spring.profiles.active");
            Properties properties_dev = FileUtil.readProperties(classPath + "/" + "application-" + env + ".properties");
            port = properties_dev.getProperty("server.port");
            context = properties_dev.getProperty("server.servlet.context-path");
        } else {
            //跳过微服务的处理
            JSONObject josnM = FileUtil.readYml(classPath + "/" + "bootstrap.yml");
            if (josnM != null) {
                String env = JsonUtil.getStrValue(josnM, "$.spring.profiles.active", "");
                JSONObject yml_devM = FileUtil.readYml(classPath + "/" + "bootstrap-" + env + ".yml");
                context = JsonUtil.getStrValue(yml_devM, "$.spring.application.name", "");
            }
        }

        context = StrUtil.trim(context, '/');
        String host = "http://" + (StringUtils.isBlank(context) ? "" : context + ".api.") + "snyq.ltd";
        String start = host + (StringUtils.isBlank(port) ? "/" : ":" + port + "/") + context;
        start = StrUtil.trim(start, '/');
        ipPortMap.put(classPath, start);
        return start;
    }

    private static String preClassPath(String realPath, String className) {
        String path = realPath;
        if (className.startsWith("com.share.dubbo.es.model")) {
            path = realPath.replaceAll("share-dubbo-es-web", "share-dubbo-es-model");
        }
        if (!path.equals(realPath)) {
            log.error("发生了路径替换path={},realPath-{}", path, realPath);
        }
        return path;
    }

    private static List<String> analysis(String http, Class<?> aClass) {
        List<String> list = new LinkedList<>();

        /*1.参数校验*/
        String cpath = "";
        Controller controller = aClass.getAnnotation(Controller.class);
        RestController restController = aClass.getAnnotation(RestController.class);
        if (controller != null) {
            cpath = controller.value();
        } else if (restController != null) {
            cpath = restController.value();
        } else {
            return list;
        }
        //方法处理
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            String cmethod = "";
            RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            if (methodMapping != null) {
                cmethod = StrUtil.valueOf(methodMapping.value());
            } else if (postMapping != null) {
                cmethod = StrUtil.valueOf(postMapping.value());
            } else if (getMapping != null) {
                cmethod = StrUtil.valueOf(getMapping.value());
            }
            if (StringUtils.isBlank(cmethod)) {
                continue;
            }

            StringBuffer sb = new StringBuffer();
            sb.append(http);
            String tmp = StrUtil.trim(cpath, '/');
            if (StringUtils.isNotBlank(tmp)) {
                sb.append("/" + tmp);
            }
            tmp = StrUtil.trim(cmethod, '/');
            if (StringUtils.isNotBlank(tmp)) {
                sb.append("/" + tmp);
            }
            list.add(sb.toString());
        }
        return list;
    }
}
